/*
OpenMVL Matrix Vector Library
Copyright (c) 2009 FluidInteractive (R)
 
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising
from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
 
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
 
Written by: Nicola Candussi <nicola@fluidinteractive.com>
*/

//layout_skew_symmetric.hpp

#ifndef MVL_LAYOUT_SKEW_SYMMETRIC_HPP
#define MVL_LAYOUT_SKEW_SYMMETRIC_HPP

#include "layout.hpp"

namespace mvl {

namespace layout {

template<typename I, typename J, typename Tag, typename Neg, typename Src>
struct skew_symmetric_entry {
    typedef Tag     tag;           //type of entry
    typedef I       i;             //(i,j) location
    typedef J       j;
    typedef Neg     neg;
    typedef Src     src;
    typedef skew_symmetric_entry   type;
};

template<typename L>
struct layout_skew_symmetric 
{
    //assume that is L is a N x 1 column vector in column major order 
    // the output layout is always row-major N x N

    typedef L layout_t;
    typedef row_major_tag order;

    typedef layout::entry<meta::int_<0>, meta::int_<0>, layout::_0_> dummy_entry; 

    typedef typename layout::begin2<
        typename layout::begin1<layout_t>::type
    >::type begin2;
    typedef typename layout::end2<
        typename layout::begin1<layout_t>::type
    >::type end2;

    template<typename A1, typename A2, typename A3, typename It, typename Condition>
    struct state {
        typedef A1 a1;
        typedef A2 a2;
        typedef A3 a3;
        typedef It it;
        typedef Condition condition;
        typedef state type;
    };

    struct update {
        template<typename State>
        struct apply {
            typedef typename meta::deref<typename State::it>::type e;
            typedef typename meta::distance<
                begin2, 
                typename State::it
             >::type pos2;
            typedef typename meta::if_<
                typename meta::equal_to<pos2, meta::int_<0> >::type,
                e,
                typename State::a1
            >::type new_a1;
            
            typedef typename meta::if_<
                typename meta::equal_to<pos2, meta::int_<1> >::type,
                e,
                typename State::a2
            >::type new_a2;

            typedef typename meta::if_<
                typename meta::equal_to<pos2, meta::int_<2> >::type,
                e,
                typename State::a3
            >::type new_a3;
            typedef typename meta::next<typename State::it>::type new_it;
            typedef typename meta::not_equal_to<new_it, end2>::type new_condition;
            typedef state<new_a1, new_a2, new_a3, new_it, new_condition> type;
        };
    };

    //get the entries from the source vector
    typedef typename meta::while_<
        state<
            dummy_entry,
            dummy_entry,
            dummy_entry,
            begin2,
            typename meta::not_equal_to<begin2, end2>::type
        >,
        update
    >::type a_entries;

    //the output layout is in row-major src_order
    //row 1
    typedef typename meta::eval_if<
        typename meta::is_same<typename a_entries::a3::tag, layout::_0_>::type,
        meta::vector<>,
        typename meta::push_back<
            meta::vector<>, 
            skew_symmetric_entry<
                meta::int_<0>,
                meta::int_<1>,
                typename layout::neg_tag<typename a_entries::a3::tag>::type,
                meta::bool_<true>,
                typename a_entries::a3
            >
        >
    >::type row_1_1;

    typedef typename meta::eval_if<
        typename meta::is_same<typename a_entries::a2::tag, layout::_0_>::type,
        meta::identity<row_1_1>,
        typename meta::push_back<
            row_1_1, 
            skew_symmetric_entry<
                meta::int_<0>,
                meta::int_<2>,
                typename a_entries::a2::tag,
                meta::bool_<false>,
                typename a_entries::a2
            >
        >
    >::type row_1;

    //row_2
    typedef typename meta::eval_if<
        typename meta::is_same<typename a_entries::a3::tag, layout::_0_>::type,
        meta::vector<>,
        typename meta::push_back<
            meta::vector<>, 
            skew_symmetric_entry<
                meta::int_<1>,
                meta::int_<0>,
                typename a_entries::a3::tag,
                meta::bool_<false>,
                typename a_entries::a3
            >
        >
    >::type row_2_1;

    typedef typename meta::eval_if<
        typename meta::is_same<typename a_entries::a1::tag, layout::_0_>::type,
        meta::identity<row_2_1>,
        typename meta::push_back<
            row_2_1, 
            skew_symmetric_entry<
                meta::int_<1>,
                meta::int_<2>,
                typename layout::neg_tag<typename a_entries::a1::tag>::type,
                meta::bool_<true>,
                typename a_entries::a1
            >
        >
    >::type row_2;

    //row_3
    typedef typename meta::eval_if<
        typename meta::is_same<typename a_entries::a2::tag, layout::_0_>::type,
        meta::vector<>,
        typename meta::push_back<
            meta::vector<>, 
            skew_symmetric_entry<
                meta::int_<2>,
                meta::int_<0>,
                typename a_entries::a2::tag,
                meta::bool_<true>,
                typename a_entries::a2
            >
        >
    >::type row_3_1;

    typedef typename meta::eval_if<
        typename meta::is_same<typename a_entries::a1::tag, layout::_0_>::type,
        meta::identity<row_3_1>,
        typename meta::push_back<
            row_3_1, 
            skew_symmetric_entry<
                meta::int_<2>,
                meta::int_<1>,
                typename layout::neg_tag<typename a_entries::a1::tag>::type,
                meta::bool_<false>,
                typename a_entries::a1
            >
        >
    >::type row_3;

    typedef meta::tvector<
        meta::vector<>,
        typename L::Rows
    > entries_0;

    typedef typename meta::replace_element<
        entries_0,
        meta::int_<0>,
        row_1
    >::type entries_1; 

    typedef typename meta::replace_element<
        entries_1,
        meta::int_<1>,
        row_2
    >::type entries_2; 

    typedef typename meta::replace_element<
        entries_2,
        meta::int_<2>,
        row_3
    >::type entries; 

   // typedef typename entries::boh b;

    typedef sparse_layout<L::Rows::value, L::Rows::value, row_major_tag, entries, 6 > type;
};

} //namespace layout

} //namespace mvl

#endif
